/*
 Reprap heater funtions based on Sprinter

 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */

/*
 This softwarepart for Heatercontrol is based on Sprinter
 big thanks to kliment (https://github.com/kliment/Sprinter)
*/


#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "Configuration.h"
#include "heater.h"
#include "Sprinter.h"
#include "timer0.h"
#include "templates.h"
#include "PrinterSettings.h"

heater_val_t extruder_heaters[2];
heater_val_t bed_heater;

uint32_t previous_millis_heater;
uint32_t previous_millis_bed_heater;
//uint32_t previous_millis_monitor;

int temp_iState_min = 256L * -PID_INTEGRAL_DRIVE_MAX / PID_IGAIN;
int temp_iState_max = 256L * PID_INTEGRAL_DRIVE_MAX / PID_IGAIN;


#ifndef EXTRUDER_HEATER_CURRENT_MAX
	#define EXTRUDER_HEATER_CURRENT_MAX 255
#endif

#ifndef BED_HEATER_CURRENT_MAX
	#define BED_HEATER_CURRENT_MAX
#endif

#ifdef MINTEMP
	int minttemp = temp2analogh(MINTEMP);
#endif

#ifdef MAXTEMP
	int maxttemp = temp2analogh(MAXTEMP);
#endif

//==========================================================================

void updatePID()
{
	temp_iState_min = (256L * -PID_INTEGRAL_DRIVE_MAX) / settings.PID_Ki;
	temp_iState_max = (256L * PID_INTEGRAL_DRIVE_MAX) / settings.PID_Ki;
}

//========================================================================== 

#include "HardwareSerial.h"

void manage_heaters()
{
	//
	// PROCESS ONLY IF HEATER EXTRUDER 1 OR 2 IS MOUNTED
	//
	#if defined(EXT1_HEATER_ACTIVE) || defined(EXT2_HEATER_ACTIVE)
		if(millis() - previous_millis_heater < HEATER_CHECK_INTERVAL )
			return;
		
		previous_millis_heater = millis();
  
		#ifdef EXT1_HEATER_ACTIVE
			adc_start_conversion(0);	// channel 1 for extruder 1
			extruder_heaters[EXT1].current_raw = 1023 - adc_get_result();	// ad values are prepared
			
			#ifdef EXT2_HEATER_ACTIVE
			adc_start_conversion(1);
			#endif

			extruder_heaters[EXT1].current_temp = analog2temp(extruder_heaters[EXT1].current_raw);
			pid(&extruder_heaters[EXT1]);
			set_heater1_pwm(extruder_heaters[EXT1].pwm);
		#endif

		#ifdef EXT2_HEATER_ACTIVE
			#ifndef EXT1_HEATER_ACTIVE
			adc_start_conversion(1);
			#endif

			extruder_heaters[EXT2].current_raw = 1023 - adc_get_result();  

			extruder_heaters[EXT2].current_temp = analog2temp(extruder_heaters[EXT2].current_raw);
			pid(&extruder_heaters[EXT2]);
			set_heater2_pwm(extruder_heaters[EXT2].pwm);
		#endif
	#endif	      
		
	//
	// PROCESS ONLY IF BED IS ACTIVE(MOUNTED)
	//
	#ifdef BED_HEATER_ACTIVE
		// BED TEMP CONTROL    
		if(millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL)
			return;
  
		previous_millis_bed_heater = millis();

		adc_start_conversion(2);
		bed_heater.current_raw = 1023 - adc_get_result();
		bed_heater.current_temp = analog2tempBed(bed_heater.current_raw);

		#ifdef MINTEMP
			if (bed_heater.current_raw < minttemp)
				bed_heater.target_temp = bed_heater.target_raw = 0;
		#endif

		#ifdef MAXTEMP
			if (bed_heater.current_raw > maxttemp)
				bed_heater.target_temp = bed_heater.target_raw = 0;
		#endif

		if (bed_heater.target_raw > bed_heater.current_raw)
			set_bed_heater_pwm(BED_HEATER_CURRENT_MAX);
		else
			set_bed_heater_pwm(0);
	#endif
}

//=======================================================================================================

#if defined (HEATER_USES_THERMISTOR) || defined (BED_USES_THERMISTOR)
int temp2analog_thermistor(int celsius, const short table[][2], int numtemps) 
{
    int raw = 0;
    uint8_t i;
    
    for (i=1; i<numtemps; i++)
    {
      if (table[i][1] < celsius)
      {
        raw = table[i-1][0] + 
          (celsius - table[i-1][1]) * 
          (table[i][0] - table[i-1][0]) /
          (table[i][1] - table[i-1][1]);
      
        break;
      }
    }

    // Overflow: Set to last value in the table
    if (i == numtemps) raw = table[i-1][0];

    return 1023 - raw;
}
#endif

//============================================================================================================

#if defined (HEATER_USES_THERMISTOR) || defined (BED_USES_THERMISTOR)
int analog2temp_thermistor(int raw,const short table[][2], int numtemps) {
    int celsius = 0;
    uint8_t i;
    
    raw = 1023 - raw;

    for (i=1; i<numtemps; i++)
    {
      if (table[i][0] > raw)
      {
        celsius  = table[i-1][1] + 
          (raw - table[i-1][0]) * 
          (table[i][1] - table[i-1][1]) /
          (table[i][0] - table[i-1][0]);

        break;
      }
    }

    // Overflow: Set to last value in the table
    if (i == numtemps) celsius = table[i-1][1];

    return celsius;
}
#endif

//====================================================================================================================

#ifdef EXTRUDERFAN_PIN
unsigned long lastExtruderCheck = 0;

void extruderFan()
{
	if (millis() - lastExtruderCheck) >= 2500) //Not a time critical function, so we only check every 2500ms
	{
		lastExtruderCheck = millis();
           
		if (analog2temp(current_raw) < EXTRUDERFAN_DEC)
		{
			WritePin(EXTRUDERFAN_PIN, LOW); //... turn the fan off
		}
		else
		{
			WritePin(EXTRUDERFAN_PIN, HIGH); //... turn the fan on
		}
	}
}
#endif

//=====================================================================================================================

void pid(heater_val_t *ptr)
{
	#ifdef MINTEMP
		if(ptr->current_raw < minttemp);
			ptr->pwm = 0;
	#endif

	#ifdef MAXTEMP
		if (ptr->current_raw > maxttemp)
			ptr->pwm = 0;
	#endif

	int16_t error = ptr->target_temp - ptr->current_temp;
	int delta_temp = ptr->current_temp - ptr->prev_temp;
	ptr->prev_temp = ptr->current_temp;

	int16_t pTerm = ((long)settings.PID_Kp * error) / 256;
	const int H0 = min(HEATER_DUTY_FOR_SETPOINT(ptr->target_temp),EXTRUDER_HEATER_CURRENT_MAX);
	int16_t heater_duty = H0 + pTerm;
	
	if(error < 30)
	{
		ptr->iState += error;
		ptr->iState = limit(ptr->iState, temp_iState_min, temp_iState_max);
		int16_t iTerm = ((long)settings.PID_Ki * ptr->iState) / 256;
		heater_duty += iTerm;
	}
	
	int prev_error = abs(ptr->target_temp - ptr->prev_temp);
	int log3 = 1; // discrete logarithm base 3, plus 1
	
	if(prev_error > 81){ prev_error /= 81; log3 += 4; }
	if(prev_error >  9){ prev_error /=  9; log3 += 2; }
	if(prev_error >  3){ prev_error /=  3; log3 ++;   }
	
	int16_t dTerm = ((long)settings.PID_Kd * delta_temp) / (256*log3);
	heater_duty += dTerm;
	
	ptr->pwm = limit(heater_duty, 0, EXTRUDER_HEATER_CURRENT_MAX);
}

//===========================================================================
